<?php


namespace app\model\MiniHome\AuthManage;


use app\model\MiniHome\Common\MiniManageModel;
use support\Db;
use support\Redis;

class LoginModel
{
//用户密码盐值
    protected static $key = "ScarecrowAuthKey";

    //用户Cache缓存KEY前缀
    protected static $cachePrefix = "SET_MINI_USER_INFO_";

    //缓存失效时间 S
    protected static $expireTime = 86400;

    //默认API调用次数
    public static $callNum = 20000;

    //设置请求来源 3:MiniHome
    public static $src = 3;

    //类型 1:微信小程序 2:微信公众号 3:微信Oauth2.0 4:其他
    public static $type = 1;

    //预登录
    public static function getOpenidSessionKeyNew($request)
    {
        $reqTime = time();
        if (env('APP_API_POST') && env('APP_ENV') == 'local' || env('APP_ENV') == 'production' || env('APP_ENV') == 'pre' ) {

            if ($request['platform'] == 'H5WeChat') {
                $miniModel = new MiniManageModel();

                $sessionInfo = $miniModel->code2Session($request['code']);

                error_log('getOpenidSessionKey=>' . json_encode($sessionInfo) . "\r\n\r\n", 3, env('LOG_ROOT') . '/getOpenidSessionKey-' . date('Y-m-d') . '.log');
                if ($sessionInfo['code'] != RETURN_SUCCESS) {
                    return $sessionInfo;
                }

                $openId = $sessionInfo['data']['openId'];
            }elseif ($request['platform'] == 'android'){
                //没有接sdk
                if (isset($request['pid']) && !empty($request['pid'])){
                    $openId = $request['pid'];
                }else{
                    $openId = $reqTime;
                }
            }
            else {
                if (empty($request['pid'])) {
                    return ModelReturn(RETURN_FAIL, 'login error');
                }
                $openId = $request['pid'];
            }

        } else {
            return ModelReturn(RETURN_SESSION_KEY_EXPIRE, '预登录失败');
        }


        $userObj = DbObjectToArr(Db::table('wa_user_oauth')
            ->where('open_id',$openId)
            ->first());

        if ($userObj){
            $user_oauth_uid = $userObj['user_id'];
            $tokenInfo = CreateToken($user_oauth_uid);
            $cacheToken = self::$cachePrefix . $tokenInfo['token'];

            $expireTime = $reqTime + self::$expireTime;
            $cacheData = [
                'id' => $user_oauth_uid,
                'uid' => $userObj['user_id'],
                'token' => $tokenInfo['token'],
                'expire_time' => $expireTime,
                'update_time' => $reqTime,
                'call_num' => self::$callNum,
                'src' => self::$src,
                'login_time' => $reqTime,
            ];

            $oldToken = Db::table('wa_token_cache')->whereRaw("uid=? AND src=?", [$userObj['user_id'], self::$src])->first();
            $oldToken = DbObjectToArr($oldToken);

            if ($oldToken) {
                Redis::del(self::$cachePrefix . $oldToken['token']);
                Db::table('wa_token_cache')->whereRaw("uid=? AND src=?", [$userObj['user_id'], self::$src])->update($cacheData);
            } else {
                Db::table('wa_token_cache')->insert($cacheData);
            }

            $userObj['tokenInfo'] = $cacheData;

            Redis::set($cacheToken, json_encode($userObj),'ex', self::$expireTime);
            defined('APP_USER_TOKE_SRC') or define('APP_USER_TOKE_SRC', self::$src);
            if (env("APP_ENV") !== "testing") {
                $cacheData['token'] = $tokenInfo['cryptToken'];
            } else {
                $cacheData['cryptToken'] = $tokenInfo['cryptToken'];
            }
            return ModelReturn(RETURN_SUCCESS, '注册成功',['user_id' => $user_oauth_uid, 'novice' => 0, 'token' => $cacheData['token']]);
        }

        $insertData = [
            'nike_name' =>  $openId,
            'username'  =>  $openId,
            'password'  =>  123456,
            'is_supper_admin'   =>  0
        ];
        try {
            Db::beginTransaction();
            $userId = Db::table('wa_user')->insertGetId($insertData);
            $weChatInfo = [
                'user_id'   =>  $userId,
                'open_id'   =>  $openId,
                'type'      =>  self::$type
            ];
            Db::table('wa_user_oauth')->insert($weChatInfo);

            Db::commit();


            $tokenInfo = CreateToken($userId);

            $cacheToken = self::$cachePrefix . $tokenInfo['token'];

            $expireTime = $reqTime + self::$expireTime;
            $cacheData = [
                'id' => $userId,
                'token' => $tokenInfo['token'],
                'expire_time' => $expireTime,
                'update_time' => $reqTime,
                'call_num' => self::$callNum,
                'src' => self::$src,
                'login_time' => $reqTime,
            ];

            $userObj['tokenInfo'] = $cacheData;
            Redis::set($cacheToken, json_encode($userObj),'ex', self::$expireTime);

            defined('APP_USER_TOKE_SRC') or define('APP_USER_TOKE_SRC', self::$src);

            return ModelReturn(RETURN_SUCCESS, '注册成功',['user_id' => $userId, 'novice' => 1, 'token' => $cacheData['token']]);
        } catch (\Exception $e) {
            Db::rollBack();
            return ModelReturn($e->getCode(), '注册失败:' . $e->getMessage());
        }
    }


    /**
     * 登录用户
     * @param $code
     * @return array
     * @throws \Exception
     */
    public static function LoginUser($code)
    {
        $m = new MiniManageModel();
        $sessionInfo = $m->code2Session($code);
        var_dump($sessionInfo);
        if ($sessionInfo['code'] != RETURN_SUCCESS) {
            return $sessionInfo;
        }

        $openId = $sessionInfo['data']['openId'];

        Db::table('wa_user_oauth')->where('open_id', $openId)->update([
            'session_key'   =>  $sessionInfo['data']['sessionKey'],
            'last_login_time'   =>  time()
        ]);

        $userObj = Db::table('wa_user as t1')->leftJoin('wa_user_oauth as t2', 't1.id', '=' ,'t2.user_id')->where(['t2.open_id' => $openId, "t2.type"=>self::$type])->select("t1.*")->first();
        if (!$userObj) {
            return ModelReturn(RETURN_FAIL, '用户名不存在');
        }
        $userObj = DbObjectToArr($userObj);
        $tokenInfo = CreateToken($userObj['id']);
        $cacheToken = self::$cachePrefix . $tokenInfo['token'];
        unset($userObj['password']);

        $nowTime = time();
        $expireTime = $nowTime + self::$expireTime;
        $cacheData = [
            'uid'	=>	$userObj['id'],
            'token'	=>	$tokenInfo['token'],
            'expire_time'	=>	$expireTime,
            'update_time'	=>	$nowTime,
            'call_num'		=>	self::$callNum,
            'is_invalid'	=>	0,
            'src'           =>  self::$src,
            'login_time'    =>  $nowTime
        ];

        $userObj['tokenInfo'] = $cacheData;
        //如果需要路由权限，则在此处进行绑定
        $userObj['authList'] = self::getUserAuthList($userObj['id']);
        $userObj['openId'] = $openId;
        try {
            Db::beginTransaction();
            $oldToken = Db::table('wa_token_cache')->whereRaw("uid=? AND src=?", [$userObj['id'], self::$src])->first();
            $oldToken = DbObjectToArr($oldToken);
            if ($oldToken) {
                Redis::del(self::$cachePrefix . $oldToken['token']);
                Db::table('wa_token_cache')->whereRaw("uid=? AND src=?", [$userObj['id'], self::$src])->update($cacheData);
            } else {
                Db::table('wa_token_cache')->insert($cacheData);
            }
            Redis::set($cacheToken, json_encode($userObj), 'ex',self::$expireTime);
            defined('APP_USER_TOKE_SRC') or define('APP_USER_TOKE_SRC', self::$src);

            if (env("APP_ENV") !== "testing") {
                $cacheData['token'] = $tokenInfo['cryptToken'];
            } else {
                $cacheData['cryptToken'] = $tokenInfo['cryptToken'];
            }
            Db::commit();
            return ModelReturn(RETURN_SUCCESS, '登录成功', $cacheData);
        } catch (\Throwable $e) {
            Db::rollBack();
            if (config('app.debug')) {
                return ModelReturn($e->getCode(), $e->getMessage());
            } else {
                return ModelReturn($e->getCode(), "登录失败");
            }
        }
    }

    /**
     * 获取用户信息
     * @param $token
     * @param $key
     * @param string $default
     * @param bool $msutDb
     * @return array
     */
    public static function getUserInfo($token, $key = '', $default = "", $msutDb = false)
    {
        $cacheToken = self::$cachePrefix . $token;
        $userInfoStr = Redis::get($cacheToken);
        if (!$msutDb && $userInfoStr) {
            $userInfo = json_decode($cacheToken,true);
            if ($userInfo) {
                if ($key) {
                    $keyList = explode('.', $key);
                    $keyList = array_filter($keyList);
                    $tempData = $userInfo;
                    foreach ($keyList as $item) {
                        if (isset($tempData[$item])) {
                            $tempData = $tempData[$item];
                        } else {
                            return ModelReturn(RETURN_SUCCESS, '获取成功', $default);
                        }
                    }
                    return ModelReturn(RETURN_SUCCESS, '获取成功', $tempData);
                } else {
                    return ModelReturn(RETURN_SUCCESS, '获取成功', $userInfo);
                }
            }
        }

        $nowTime = time();
        $userObj = Db::table('wa_token_cache as t1')->leftJoin('wa_user as t2', 't2.id', '=', 't1.uid')->whereRaw("t1.expire_time>{$nowTime} AND t1.call_num>0 AND t1.token=? AND t1.is_invalid=0 AND t1.src=?", [$token, self::$src])->select(['t2.*', 't1.uid', 't1.token', 't1.expire_time', 't1.update_time', 't1.call_num', 't1.is_invalid'])->first();
        if (!$userObj) {
            return ModelReturn(RETURN_CHECK_DATA_DB_FAIL, '用户不存在');
        }
        $userObj = DbObjectToArr($userObj);
        unset($userObj['password']);

        $tokenInfo = [
            'uid'	=>	$userObj['uid'],
            'token'	=>	$userObj['token'],
            'expire_time'	=>	$userObj['expire_time'],
            'update_time'	=>	time(),
            'call_num'		=>	$userObj['call_num'],
            'is_invalid'	=>	$userObj['is_invalid']
        ];

        $userObj['tokenInfo'] = $tokenInfo;
        //如果需要路由权限，则在此处进行绑定
        $userObj['authList'] = self::getUserAuthList($userObj['id']);
        Redis::set($cacheToken, json_encode($userObj),'ex', self::$expireTime);

        $userInfo = $userObj;
        if ($userInfo) {
            if ($key) {
                $keyList = explode('.', $key);
                $keyList = array_filter($keyList);
                $tempData = $userInfo;
                foreach ($keyList as $item) {
                    if (isset($tempData[$item])) {
                        $tempData = $tempData[$item];
                    } else {
                        return ModelReturn(RETURN_SUCCESS, '获取成功', $default);
                    }
                }
                return ModelReturn(RETURN_SUCCESS, '获取成功', $tempData);
            }
        }

        return ModelReturn(RETURN_SUCCESS, '获取成功', $userObj);
    }

    /**
     * 更新token
     * @param $token
     * @return array
     * @throws \Exception
     */
    public static function updateToken($token)
    {
        $userInfo = self::getUserInfo($token);
        if ($userInfo['code'] !== RETURN_SUCCESS) {
            return $userInfo;
        }

        $userObj = $userInfo['data'];
        $newTokenInfo = CreateToken($userObj['id']);
        $expireTime = time() + self::$expireTime;
        $updateTokenInfo = [
            'token'	=>	$newTokenInfo['token'],
            'expire_time'	=>	$expireTime,
            'update_time'	=>	time(),
            'call_num'		=>	self::$callNum,
            'is_invalid'	=>	0,
            'src'           =>  self::$src
        ];

        $info = self::updateTokenCache($token, $updateTokenInfo);
        if ($info['code'] === RETURN_SUCCESS) {
            if (env("APP_ENV") !== "testing") {
                $updateTokenInfo['token'] = $newTokenInfo['cryptToken'];
            } else {
                $updateTokenInfo['cryptToken'] = $newTokenInfo['cryptToken'];
            }

            return ModelReturn(RETURN_SUCCESS, '更新TOKEN成功', $updateTokenInfo);
        }
        return $info;
    }

    /**
     * 更新token信息
     * @param $token
     * @param array $updateData
     * @param bool $isUpdateDb
     * @return array|array
     * @throws \Exception
     */
    public static function updateTokenCache($token, $updateData = [], $isUpdateDb = true)
    {
        $userInfo = self::getUserInfo($token);
        if ($userInfo['code'] != RETURN_SUCCESS) {
            return $userInfo;
        }

        $userObj = $userInfo['data'];
        $tokenInfo = $userObj['tokenInfo'];
        foreach ($updateData as $key => $value) {
            isset($tokenInfo[$key]) && $tokenInfo[$key] = $value;
        }
        $tokenInfo['update_time'] = time();
        $userObj['tokenInfo'] = $tokenInfo;
        $cacheToken = self::$cachePrefix . $token;
        if (!Db::table('wa_token_cache')->whereRaw("token=? AND src=?", [$token, self::$src])->first()) {
            return ModelReturn(RETURN_FAIL, "更新Token缓存信息失败");
        }

        try {
            Redis::set($cacheToken, json_encode($userObj),'ex', self::$expireTime);
            $isUpdateDb && Db::table('wa_token_cache')->whereRaw("token=? AND src=?", [$token, self::$src])->update($userObj['tokenInfo']);
            return ModelReturn(RETURN_SUCCESS, '更新成功', $userObj);
        } catch (\Throwable $e) {
            self::LoginOut($token);
            if (config('app.debug')) {
                return ModelReturn($e->getCode(), $e->getMessage());
            } else {
                return ModelReturn($e->getCode(), "更新Token缓存信息失败");
            }
        }
    }

    /**
     * 退出登录
     * @param $token
     * @return array
     * @throws \Exception
     */
    public static function LoginOut($token)
    {
        $cacheToken = self::$cachePrefix . $token;
        Redis::del($cacheToken);
        Db::table('wa_token_cache')->whereRaw("token=? AND src=?", [$token, self::$src])->update([
            'is_invalid'	=>	1,
            'update_time'	=>	time()
        ]);
        return ModelReturn(RETURN_SUCCESS, '退出登录成功');
    }

    /**
     * 用户注册
     * @param $userName
     * @param $userPwd
     * @return array
     */
    public static function register($loginCode, $phoneCode) {
        $miniModel = new MiniManageModel();
        $sessionInfo = $miniModel->code2Session($loginCode);
        if ($sessionInfo['code'] != RETURN_SUCCESS) {
            return $sessionInfo;
        }

        $phoneInfo = $miniModel->getPhoneNumber($phoneCode);
        if ($phoneInfo['code'] != RETURN_SUCCESS) {
            return $phoneInfo;
        }

        $openId = $sessionInfo['data']['openId'];
        $phone = $phoneInfo['data']['phone_info']['phoneNumber'];

        if (!$phone) {
            return ModelReturn(RETURN_CHECK_PARAMS_FAIL, '用户手机获取失败，注册失败');
        }

        if(Db::table('wa_user_oauth')->where('open_id', $openId)->first()) {
            return ModelReturn(RETURN_CHECK_DATA_DB_FAIL, '该微信已注册，请勿重复注册');
        }

        $userName = $phone;
        $userPwd = $phone;
        $userObj = Db::table('wa_user')->where("username", $userName)->first();
        if ($userObj) {
            return ModelReturn(RETURN_CHECK_DATA_DB_FAIL, '该登录账户已存在,请修改登录账户进行注册');
        }
        $checkPassword = md5(self::$key . $userPwd . self::$key);
        $insertData = [
            'nike_name' =>  substr($userName, 0, 10),
            'username'  =>  $userName,
            'password'  =>  $checkPassword,
            'is_supper_admin'   =>  0
        ];
        try {
            Db::beginTransaction();
            $userId = Db::table('wa_user')->insertGetId($insertData);
            $weChatInfo = [
                'user_id'   =>  $userId,
                'open_id'   =>  $openId,
                'type'      =>  self::$type
            ];
            Db::table('wa_user_oauth')->insert($weChatInfo);
            Db::commit();
            return ModelReturn(RETURN_SUCCESS, '注册成功');
        } catch (\Exception $e) {
            Db::rollBack();
            return ModelReturn($e->getCode(), '注册失败:' . $e->getMessage());
        }
    }

    /**
     * 解密微信数据
     * @param $token
     * @param $encryptedData
     * @param $iv
     * @return array
     */
    public static function decryptData($token, $encryptedData, $iv) {
        $openIdInfo = self::getUserInfo($token, 'openId');
        if ($openIdInfo['code'] != RETURN_SUCCESS) {
            return $openIdInfo;
        }
        $m = new MiniManageModel($openIdInfo['data']);
        return $m->decryptData($encryptedData, $iv);
    }

    /**
     * 获取微信手机号
     * @param $token
     * @param $code
     * @return array|string
     */
    public static function getWeChatPhoneNumber($token, $code) {
        $openIdInfo = self::getUserInfo($token, 'openId');
        if ($openIdInfo['code'] != RETURN_SUCCESS) {
            return $openIdInfo;
        }
        $m = new MiniManageModel($openIdInfo['data']);
        return $m->getPhoneNumber($code);
    }
}